# -*- coding: utf-8 -*-
# @Time     : 2023/12/6 09:39
# @Author   : Long-Long Qiu
# @FileName : test2.py
# @Product  : PyCharm
# import packages
import copy
import json
import math
import pickle
from py3dbp import Packer, Bin, Item, Painter
import matplotlib.pyplot as plt
import time
import hashlib
import base64
from PlotPainter import PlotPainter
from matplotlib.patches import Circle
import plotly.graph_objects as go
import numpy as np

preMinNum = 20
topNum = 5


def md5_encode(text):
    """
    对文本进行md5加密
    """
    md5_hash = hashlib.md5()
    md5_hash.update(text.encode('utf-8'))
    encoded_text = md5_hash.hexdigest()
    return encoded_text


def image_to_base64(image_path):
    with open(image_path, "rb") as image_file:
        image_data = image_file.read()
        base64_data = base64.b64encode(image_data)
        base64_string = base64_data.decode("utf-8")
        return base64_string


def get_min_m(n, k):
    """
    n个圆柱体，最多堆成k层，求第一层最少要几个
    :param n: 圆柱体数量
    :param k: 最多堆叠层数
    :return:
    """
    for m in range(1, n + 1, 1):
        s = sum([m - j for j in range(k) if j <= m])
        if s >= n:
            return m


def generate_heap(items, max_layers, move_items=[]):
    """
    将一组圆柱体摆放成堆
    :param items:
    :param max_layers: 堆的最大层数，1,2或3
    :param move_items: 考虑是否有下一更小尺寸准一过来
    :return:
    """
    n = items[4]
    w, h, d = items[0] * 2, items[1], items[0] * 2
    weight = items[3]

    if max_layers == 2:
        n1 = n // max_layers + 1    # 第一层的item个数
        # 摆放第一层
        results1 = []
        for i in range(n1):
            results1.append([w, h, d, weight, i * w, 0, 0, items[6]])

        # 摆放第二层
        results2 = []
        n2 = n - n1
        for i in range(n2):
            results2.append([w, h, d, weight, i * w + w / 2, 0, d * math.sqrt(3) / 2, items[6]])
        if move_items:  # 堆放从更小尺寸移入的项
            for i in range(move_items[4]):
                p = (w + w / 2 + move_items[0] + w / 2 + move_items[0]) / 2
                s = math.sqrt(p * (p - w) * (p - (w / 2 + move_items[0])) * (p - (w / 2 + move_items[0])))
                h = 2 * s / w
                results2.append([move_items[0] * 2, move_items[1], move_items[0] * 2,
                                 move_items[3], n2 * w + w / 2 + i * w, 0, w / 2 + h - move_items[0], move_items[6]])

        return [results1, results2]

    elif max_layers == 3:
        n1 = min(math.ceil(n / 3 + 1), n)
        # 摆放第一层
        results1 = []
        for i in range(n1):
            results1.append([w, h, d, weight, i * w, 0, 0, items[6]])

        # 摆放第二层
        n2 = min(n - n1, n1 - 1)
        results2 = []
        for i in range(n2):
            results2.append([w, h, d, weight, i * w + w / 2, 0, d * math.sqrt(3) / 2, items[6]])
        if move_items and items[4] % 3 == 1:  # 堆放从更小尺寸移入的项
            for i in range(move_items[4]):
                p = (w + w / 2 + move_items[0] + w / 2 + move_items[0]) / 2
                s = math.sqrt(p * (p - w) * (p - (w / 2 + move_items[0])) * (p - (w / 2 + move_items[0])))
                h = 2 * s / w
                results2.append([move_items[0] * 2, move_items[1], move_items[0] * 2,
                                 move_items[3], n2 * w + w / 2 + i * w, 0, w / 2 + h - move_items[0], move_items[6]])

        # 摆放第三层
        n3 = n - n1 - n2
        results3 = []
        for i in range(n3):
            results3.append([w, h, d, weight, i * w + w, 0, d * math.sqrt(3), items[6]])
        if move_items and items[4] % 3 == 2:  # 堆放从更小尺寸移入的项
            for i in range(move_items[4]):
                p = (w + w / 2 + move_items[0] + w / 2 + move_items[0]) / 2
                s = math.sqrt(p * (p - w) * (p - (w / 2 + move_items[0])) * (p - (w / 2 + move_items[0])))
                h = 2 * s / w
                results2.append([move_items[0] * 2, move_items[1], move_items[0] * 2,
                                 move_items[3], n2 * w + w + i * w, 0, w / 2 * 3 + h - move_items[0], move_items[6]])

        return [results1, results2, results3]

    elif max_layers == 1:
        results1 = []
        for i in range(n):
            results1.append([w, h, d, weight, i * w, 0, 0, items[6]])

        return [results1]


def pack_cylinders(items, radius_threshold1, radius_threshold2, box_depth):
    """
    将圆柱体先按尺寸大小分成若干组，在每组内堆成两层或三层(根据两个半径阈值和圆柱体的半径大小确定)
    :param items: 仅包含圆柱体
    :param radius_threshold1:
    :param radius_threshold2:
    :param box_depth: 容器的高度
    :return:
    """
    colors = ['orange', 'olive', 'green', 'red', 'blue', 'purple', 'yellow']

    # 1. 先按尺寸分组并按从小到大的顺序排列
    items = sorted(items, key=lambda x: x[0], reverse=True)
    items_by_size = []
    for item in items:
        if not items_by_size:
            items_by_size.append(item + [colors[len(items_by_size) % len(colors)]])
        else:
            if items_by_size[-1][0] == item[0] and items_by_size[-1][1] == item[1]:  # 尺寸相同，合并
                items_by_size[-1][4] += item[4]
            else:
                items_by_size.append(item + [colors[len(items_by_size) % len(colors)]])

    # 2.
    move_flag = {}  # 记录原始哪些下标下的items移走了一项(或两项)
    move_item = {}  # 记录原始哪些坐标下的items移入了什么项
    for index, item in enumerate(items_by_size):
        if item[0] <= radius_threshold1:
            if item[4] == 2 or (item[4] > 3 and item[4] % 3 != 0):   # 此时，可以将下一尺寸的转移一个过来
                if index + 1 < len(items_by_size) and items_by_size[index + 1][1] <= items_by_size[index][1]:
                    move_flag[index + 1] = 1
                    move_item[index] = copy.deepcopy(items_by_size[index + 1])
                    move_item[index][4] = 1

        elif item[0] <= radius_threshold2:
            if item[4] % 2 == 0:    # 此时，可以将下一尺寸的转移一个过来
                if index + 1 < len(items_by_size) and items_by_size[index + 1][1] <= items_by_size[index][1]:
                    move_flag[index + 1] = 1
                    move_item[index] = copy.deepcopy(items_by_size[index + 1])
                    move_item[index][4] = 1

    # 3. 对每组圆柱体，形成堆
    heaps = []
    for index, item in enumerate(items_by_size):
        radius = item[0]

        # 根据当前items的高度以及box_height对max_heap_layers重新赋值
        if box_depth >= (1 + math.sqrt(3)) * 2 * radius:
            max_heap_layers = 3
        elif box_depth >= (2 + math.sqrt(3)) * radius:
            max_heap_layers = 2
        elif box_depth >= radius:
            max_heap_layers = 1
        else:
            max_heap_layers = 0

        new_item = copy.deepcopy(item)
        new_item[4] -= move_flag.get(index, 0)
        new_move_item = move_item.get(index, [])

        if radius <= radius_threshold1:
            heap = generate_heap(new_item, min(3, max_heap_layers), move_items=new_move_item)
        elif radius <= radius_threshold2:
            heap = generate_heap(new_item, min(2, max_heap_layers), move_items=new_move_item)
        else:
            heap = generate_heap(new_item, 1, move_items=new_move_item)
        heaps.append(heap)

    return heaps



def pack_cylinders_2(all_items):
    print('pc2', all_items)
    results = []
    for items in all_items:
        for i in range(items[4]):
            results.append([[[items[0], items[1], items[2], items[3], 0, 0, 0, 'orange']]])
    print('pc2', results)
    return results


def pack_cubes(cubes, box_depth, max_x=1.0):
    # 1. 将木箱、托盘、纸箱分开
    woodens = [cube for cube in cubes if cube[5] == 1]
    trays = [cube for cube in cubes if cube[5] == 2]
    cartoons = [cube for cube in cubes if cube[5] == 3]

    results = {}

    # 2. 对不同种类的货物分别做处理
    # 2.1 预打包木箱
    for wooden in woodens:
        pass

    # 2.2 预打包托盘
    for index, tray in enumerate(trays):
        layers_num = 1
        width, height, depth, weight = tray[0:4]
        num = tray[4]
        if width * height > 0.6 and 100 < weight < 600:    # 底面面积大于等于0.6m^2且重量介于100~600kg之间可堆叠两层
            layers_num = 2
        layers = [[] for _ in range(layers_num)]

        # 按照车厢高度优先在z轴方向堆叠，再在x轴方向平铺
        x, z = 0., 0.  # 记录当前x,z坐标
        h = 0  # 记录当前层数
        for i in range(num):
            layers[h].append([round(width, 2), round(height, 2), round(depth, 2),
                              round(weight, 2), round(x, 2), round(0, 2), round(z, 2)])
            if h == layers_num - 1:
                h = 0
                z = 0
                x += width
            else:
                h += 1
                z = h * depth

        results[f"tray-{index}"] = list(filter(lambda m: m != [], layers))

    # 2.3 预打包纸箱
    k = 0
    for index, cartoon in enumerate(cartoons):
        # 2.3.1 确定车高能堆叠几层
        layers_num = math.floor(box_depth // cartoon[2])
        layers = [[] for _ in range(layers_num)]
        width, height, depth, weight = cartoon[0:4]
        num = cartoon[4]

        # 按照车厢高度优先在z轴方向堆叠，再在x轴方向平铺
        x, z = 0., 0.   # 记录当前x, z坐标
        h = 0   # 记录当前层数
        for i in range(num):
            layers[h].append([round(width, 2), round(height, 2), round(depth, 2),
                              round(weight, 2), round(x, 2), round(0, 2), round(z, 2)])
            if h == layers_num - 1:
                h = 0
                z = 0
                x += width
                # if x >= max_x:
                #     results[f"cartoon-{k}"] = list(filter(lambda m: m != [], layers))
                #     k += 1
                #     layers = [[] for _ in range(layers_num)]
                #     x, z, h = 0., 0., 0
            else:
                h += 1
                z = h * depth

        results[f"cartoon-{k}"] = list(filter(lambda m: m != [], layers))
        k += 1

    return results, woodens


def pack_cubes2(cubes, box_width, box_height, box_depth, start_index=0):
    # 1. 将木箱、托盘、纸箱分开
    woodens = [cube for cube in cubes if cube[5] == 1]
    trays = [cube for cube in cubes if cube[5] == 2]
    cartoons = [cube for cube in cubes if cube[5] == 3]

    # box_width /= 5
    # box_height /= 5

    results = {}
    flag = True

    # 2. 对不同种类的货物分别做处理
    # 2.1 预打包木箱
    for wooden in woodens:
        pass

    # 2.2 预打包托盘
    other_trays = []
    # 优先在y轴方向上平铺，其次在z轴方向上堆叠，最后在x轴方向上平铺
    for index, tray in enumerate(trays):
        # 获取货物的尺寸和重量
        width, height, depth, weight = tray[0:4]
        # 确定沿着y轴和z轴能放几个货物
        y_layers_num = math.floor(box_height // height)
        z_layers_num = 1
        if width * height / 1000 / 1000 > 0.6 and 100 < weight < 600:
            z_layers_num = math.floor(box_depth // depth)
        # x_layers_num = math.floor(box_width // width) // 20
        x_layers_num = 1
        layers = [[] for _ in range(z_layers_num)]
        x, y, z = 0., 0., 0.
        num = tray[4]
        w, h, d = 0, 0, 0
        for i in range(num):
            layers[d].append([round(width, 2), round(height, 2), round(depth, 2),
                              round(weight, 2), round(x, 2), round(y, 2), round(z, 2)])

            if h == y_layers_num - 1:  # y轴铺满，往z轴堆叠或x轴平铺
                if d == z_layers_num - 1:  # y轴铺满且z轴也堆满了，则往x轴方向平铺
                    w += 1
                    h = 0
                    d = 0
                    if w >= x_layers_num and num > i:  # 车厢放不下
                        other_trays.append([width, height, depth, weight, num - i - 1, 2])
                        break
                else:  # y轴铺满而z轴未堆满，则往z轴方向堆叠
                    h = 0
                    d += 1

                x, y, z = w * width, h * height, d * depth

            else:  # y轴未铺满，沿着y轴方向继续平铺
                h += 1
                y = h * height
        results[f"tray-{index + start_index}"] = list(filter(lambda m: m != [], layers))

    # 2.3 预打包纸箱
    other_cartoons = []
    # 优先在y轴方向上平铺，其次在z轴方向上堆叠，最后在x轴方向上平铺
    for index, cartoon in enumerate(cartoons):
        # 获取货物的尺寸和重量
        width, height, depth, weight = cartoon[0:4]
        # 确定沿着y轴和z轴能放几个货物
        y_layers_num = math.floor(box_height // height)
        z_layers_num = math.floor(box_depth // depth)
        # x_layers_num = math.floor(box_width // width) // 20
        x_layers_num = 1
        layers = [[] for _ in range(z_layers_num)]
        x, y, z = 0., 0., 0.
        num = cartoon[4]
        w, h, d = 0, 0, 0
        for i in range(num):
            layers[d].append([round(width, 2), round(height, 2), round(depth, 2),
                              round(weight, 2), round(x, 2), round(y, 2), round(z, 2)])

            if h == y_layers_num - 1:   # y轴铺满，往z轴堆叠或x轴平铺
                if d == z_layers_num - 1:   # y轴铺满且z轴也堆满了，则往x轴方向平铺
                    w += 1
                    h = 0
                    d = 0
                    if w >= x_layers_num and num > i:   # 车厢放不下
                        other_cartoons.append([width, height, depth, weight, num - i - 1, 3])
                        break
                else:   # y轴铺满而z轴未堆满，则往z轴方向堆叠
                    h = 0
                    d += 1

                x, y, z = w * width, h * height, d * depth

            else:   # y轴未铺满，沿着y轴方向继续平铺
                h += 1
                y = h * height
        results[f"cartoon-{index + start_index}"] = list(filter(lambda m: m != [], layers))

    # if len(other_trays) > preMinNum or len(other_cartoons) > preMinNum:
    if other_trays or other_cartoons:
        flag = False
    return results, other_trays + woodens + other_cartoons, flag


def pack_heap(heaps, boxes, cubes=[],cube_heaps={}, use_box_height=True):
    """
    将heap装载到box中
    :param heaps:
    :param boxes:
    :param cubes:
    :param use_box_height: 是否使用bin的高度作为heap的高度(可以用于防止在heap上方继续堆叠)
    :return:
    """

    # 1. 初始化packer对象，并将box添加到packer中
    packer = Packer()
    for idx, b in enumerate(boxes):
        if type(b) == list or type(b) == tuple:
            bin = Bin(f"car-{idx}", (b[0], b[1], b[2]), b[3], corner=0, put_type=0, price=b[4])
            packer.addBin(bin)
        elif type(b) == Bin:
            packer.addBin(b)

    # 2. 添加cylinder的heap对象
    colors = ['orange', 'olive', 'green', 'red', 'blue', 'purple', 'yellow']
    for name, heap in heaps.items():
        index = int(name.split('-')[1])
        width = heap[0][-1][4] + heap[0][-1][0]
        height = heap[0][0][1]
        depth = [item for layer in heap for item in layer][-1][6] + heap[0][0][2]
        if use_box_height:
            # depth = float(packer.bins[0].depth)
            depth = max([float(ib.depth) for ib in packer.bins])
        weight = sum([item[3] for layer in heap for item in layer])

        print('cylinder-heap', width, height, depth)
        packer.addItem(
            Item(partno=f'Box-{index + 1}', name=name, typeof='heap',
                 WHD=(width, height, depth), weight=weight, level=0, loadbear=100,
                 updown=False, color=colors[index % len(colors)]))

    # 3. 添加cube的heap对象
    for name, heap in cube_heaps.items():
        if not heap:
            continue
        level_map = {'wooden': 1, 'tray': 2, 'cartoon': 3}
        # width = heap[0][-1][0] + heap[0][-1][4]
        width = max([ie[0] + ie[4] for layer in heap for ie in layer])
        # height = heap[0][-1][1] + heap[0][-1][5]
        height = max([ie[1] + ie[5] for layer in heap for ie in layer])
        depth = heap[-1][0][2] + heap[-1][0][6]
        # weight = heap[0][0][3]
        weight = sum([ie[3] for layer in heap for ie in layer])
        kind, index = name.split('-')[0], int(name.split('-')[1])
        packer.addItem(Item(partno=f"CubeBox-{index + 1}", name=name, typeof='cube',
                            WHD=(width, height, depth), weight=weight, level=level_map[kind],
                            loadbear=100, updown=False, color=colors[index % len(colors)]))

    # 4. 添加额外的cubes(1为木箱，2为托盘，3为纸箱，使用item.level进行区分)
    cube_colors = {
        1: 'purple',    # 木箱为紫色
        2: 'cyan',  # 托盘蓝绿色
        3: 'gray'   # 纸箱为灰色
    }
    cube_num = 1
    for index, cube in enumerate(cubes):
        n = cube[4]
        for _ in range(n):
            packer.addItem(Item(partno=f'Box-{cube_num + len(heaps)}', name=f'cube{cube_num + len(heaps)}', typeof='cube', WHD=(cube[0], cube[1], cube[2]),
                 weight=cube[3], level=cube[5], loadbear=cube[3],
                 updown=False, color=cube_colors[cube[5]]))
            cube_num += 1

    # 5. 装箱
    packer.pack(
        bigger_first=False,
        distribute_items=False,
        fix_point=True,
        check_stable=True,
        support_surface_ratio=1.,
        number_of_decimals=2
    )
    # put order
    packer.putOrder()
    return packer


def packing(box, items, radius_threshold1, radius_threshold2, pre_packing_cubes=True):

    # 预处理：过滤数量小于1的项
    items = list(filter(lambda x: x[4] > 0, items))
    # 1. 获取box的高度
    if type(box[0]) == list or type(box[0]) == tuple:
        # box_length = box[0][0]
        # box_width = box[0][1]
        # box_height = box[0][2]
        box_length = max([be[0] for be in box])
        box_width = max([be[1] for be in box])
        box_height = max([be[2] for be in box])
    elif type(box[0]) == Bin:
        # box_length = float(box[0].width)
        # box_width = float(box[0].height)
        # box_height = float(box[0].depth)
        box_length = float(max([be.width for be in box]))
        box_width = float(max([be.height for be in box]))
        box_height = float(max([be.depth for be in box]))
    else:
        box_length, box_width, box_height = 0, 0, 0

    cubes = list(filter(lambda x: x[5] != 0, items))    # 所有的非盘具
    items = list(filter(lambda x: x[5] == 0, items))    # 所有的盘具

    if sum([ie[4] for ie in items]) > 0:
        heaps = pack_cylinders_2(items)
        heaps = {f"heap-{index + 1}": heap for index, heap in enumerate(heaps)}
    else:
        heaps = {}

    # 2. 将货物进行打包预处理
    # 2.1 预打包盘货：将盘货items转成heaps
    # if sum([ie[4] for ie in items]) > 0:
    #     heaps = pack_cylinders(items, radius_threshold1, radius_threshold2, box_height)
    #     heaps = {f"heap-{index + 1}": heap for index, heap in enumerate(heaps)}
    # else:
    #     heaps = {}

    if pre_packing_cubes:   # 对cubes进行预打包处理
    # 2.2 预打包cubes：将cubes转成heaps
        pre_pack_cubes = pack_cubes2
        cube_heaps, other_cubes, finished = pre_pack_cubes(cubes, box_length, box_width, box_height)

        while not finished:  # 预打包未结束
            # 继续进行预打包
            new_cube_heaps, other_cubes, finished = pre_pack_cubes(other_cubes, box_length, box_width, box_height, start_index=len(cube_heaps))
            cube_heaps = {**cube_heaps, **new_cube_heaps}
        # 3. 装箱
        result = pack_heap(heaps, box, cubes=other_cubes, cube_heaps=cube_heaps)
        return {**heaps, **cube_heaps}, result
    else:
        result = pack_heap(heaps, box, cubes=cubes)
        return heaps, result


def plot_heaps(pack_result, heaps, title='', plot_heap_cube=True, elev=45, azim=40, plot_type=1):
    """
    对打包好的heaps进行绘制
    :param pack_result: pack结果
    :param heaps:
    :param title:
    :param plot_heap_cube: 是否绘制heap对应的cube
    :param elev:
    :param azim:
    :param plot_type:
    :return:
    """

    for b in pack_result.bins:
        fig = plt.figure()

        axGlob = plt.axes(projection='3d')
        painter = Painter(b)

        # 1. 绘制bin
        painter._plotCube(axGlob, 0, 0, 0, float(b.width), float(b.height), float(b.depth), color='black', mode=1, linewidth=2)

        # 2. 依次绘制heap
        for heap in b.items:

            if heap.typeof != 'heap':
                continue

            heap_name = heap.name

            # 2.1. 绘制heap所在的cube
            heap_x, heap_y, heap_z = heap.position
            if plot_heap_cube:
                painter._plotCube(axGlob, float(heap_x), float(heap_y), float(heap_z), float(heap.width),
                                  float(heap.height), float(heap.depth), color=heaps[heap_name][0][0][7], mode=1, linewidth=1, text=heap_name, fontsize=8)

            # 2.2. 绘制heap中的cylinder
            for layer in heaps[heap_name]:
                for cylinder in layer:
                    cylinder_x, cylinder_y, cylinder_z = float(heap_x) + cylinder[4], float(heap_y) + cylinder[5], float(heap_z) + cylinder[6]
                    # cylinder_x, cylinder_y, cylinder_z = cylinder[4], cylinder[5], cylinder[6]
                    painter._plotCylinder_2(axGlob, cylinder_x, cylinder_y, cylinder_z, float(cylinder[0]), float(cylinder[1]), float(cylinder[2]), color=cylinder[7],
                                         mode=2, alpha=0.5)

        # 3. 绘制额外的cubes
        for cube in b.items:
            if cube.typeof != 'cube':
                continue
            # 3.1 绘制cube
            cube_x, cube_y, cube_z = cube.position
            cube_w, cube_h, cube_d = cube.getDimension()
            cube_color = cube.color
            if plot_type == 2:
                cube_color = getCubeColor(cube.pressure)
            painter._plotCube(axGlob, float(cube_x), float(cube_y), float(cube_z), float(cube_w),
                              float(cube_h), float(cube_d), color=cube_color, mode=2, linewidth=1,
                              text='')

        # 4. 图例
        legends = []
        legends_text = []
        for heap_name, layers in heaps.items():
            color = layers[0][0][7]
            radius = layers[0][0][0] / 2
            icon_text = f"{layers[0][0][0] / 2} x {layers[0][0][1]}"
            legends.append(Circle((5, 5), radius=radius, color=color))
            legends_text.append(icon_text)
        # axGlob.legend(legends, legends_text, loc='upper left')

    painter.setAxesEqual(axGlob)
    # 调整视角
    axGlob.view_init(elev=elev,  # 仰角
                 azim=azim,  # 方位角
                 )
    plt.title(title)

    return plt


def plot_heaps_matplot(b, heaps, title='', plot_heap_cube=True, elev=45, azim=40, plot_type=1):
    """
    对打包好的heaps进行绘制
    :param pack_result: pack结果
    :param heaps:
    :param title:
    :param plot_heap_cube: 是否绘制heap对应的cube
    :param elev:
    :param azim:
    :param plot_type:
    :return:
    """
    # plt.rcParams['savefig.dpi'] = 300
    # plt.rcParams['figure.dpi'] = 300

    fig = plt.figure()

    axGlob = plt.axes(projection='3d')
    painter = Painter(b)

    # 1. 绘制bin
    painter._plotCube(axGlob, 0, 0, 0, float(b.width), float(b.height), float(b.depth), color='black', mode=1, linewidth=2)

    # 2. 依次绘制heap
    for heap in b.items:

        if heap.typeof != 'heap':
            continue

        heap_name = heap.name

        # 2.1. 绘制heap所在的cube
        heap_x, heap_y, heap_z = heap.position
        col = heaps[heap_name][0][0][7]  # 原始颜色
        if plot_type == 1:
            col = heaps[heap_name][0][0][7]
        elif plot_type == 2:
            col = getCubeColor(0.)
        if plot_heap_cube:
            painter._plotCube(axGlob, float(heap_x), float(heap_y), float(heap_z), float(heap.width),
                              float(heap.height), float(heap.depth), color='#00FF00', mode=1, linewidth=1, text=heap_name, fontsize=8)

        # 2.2. 绘制heap中的cylinder
        for layer in heaps[heap_name]:
            for cylinder in layer:
                cylinder_x, cylinder_y, cylinder_z = float(heap_x) + cylinder[4], float(heap_y) + cylinder[5], float(heap_z) + cylinder[6]
                # cylinder_x, cylinder_y, cylinder_z = cylinder[4], cylinder[5], cylinder[6]
                painter._plotCylinder_2(axGlob, cylinder_x, cylinder_y, cylinder_z, float(cylinder[0]), float(cylinder[1]), float(cylinder[2]), color=col,
                                     mode=2, alpha=0.5)

    # 3. 绘制额外的cubes
    for cube in b.items:
        if cube.typeof == 'cube':
            if cube.partno.startswith('CubeBox'):    # 预打包的cubes_heap
                heap_x, heap_y, heap_z = float(cube.position[0]), float(cube.position[1]), float(cube.position[2])
                heap_w, heap_h, heap_d = float(cube.dimension[0]), float(cube.dimension[1]), float(cube.dimension[2])
                col = getCubeColor(0.)
                if plot_type == 1:
                    if cube.name.startswith('wooden'):
                        col = 'purple'
                    elif cube.name.startswith('tray'):
                        col = 'cyan'
                    elif cube.name.startswith('cartoon'):
                        col = 'gray'
                if plot_heap_cube:
                    painter._plotCube(axGlob, heap_x, heap_y, heap_z, heap_w, heap_h, heap_d, color=col,
                                      mode=1, linewidth=2, text=cube.name, fontsize=8)
                for layer in heaps[cube.name]:
                    for son in layer:
                        son_x, son_y, son_z = son[4:7]
                        son_w, son_h, son_d = son[0:3]
                        if cube.rotation_type == 1:
                            son_w, son_h = son_h, son_w
                            son_x, son_y = son_y, son_x
                        painter._plotCube(axGlob, heap_x + son_x, heap_y + son_y, heap_z + son_z,
                                          son_w, son_h, son_d, color=col, mode=2, linewidth=1,
                                          text='')

            else:   # 绘制零散的cube
                # 3.1 绘制cube
                cube_x, cube_y, cube_z = cube.position
                cube_w, cube_h, cube_d = cube.dimension
                cube_color = cube.color
                if plot_type == 2:
                    cube_color = getCubeColor(cube.pressure)
                painter._plotCube(axGlob, float(cube_x), float(cube_y), float(cube_z), float(cube_w),
                                  float(cube_h), float(cube_d), color=cube_color, mode=2, linewidth=1,
                                  text='')

    # 4. 图例
    # legends = []
    # legends_text = []
    # for heap_name, layers in heaps.items():
    #     color = layers[0][0][7]
    #     radius = layers[0][0][0] / 2
    #     icon_text = f"{layers[0][0][0] / 2} x {layers[0][0][1]}"
    #     legends.append(Circle((5, 5), radius=radius, color=color))
    #     legends_text.append(icon_text)
    # axGlob.legend(legends, legends_text, loc='upper left')

    painter.setAxesEqual(axGlob)
    # 调整视角
    axGlob.view_init(elev=elev,  # 仰角
                 azim=azim,  # 方位角
                 )
    plt.title(title)

    return plt, fig


def getCubeColor(pressure):
    if pressure == 0:
        return '#CCCCE5'
    elif pressure <= 150:
        return '#B2B2FA'
    elif pressure <= 300:
        return '#99B7FF'
    elif pressure <= 450:
        return '#7FD7FF'
    elif pressure <= 600:
        return '#7EFFE1'
    elif pressure <= 750:
        return '#A3FFA1'
    elif pressure <= 900:
        return '#D7FF54'
    elif pressure <= 1050:
        return '#FFC919'
    elif pressure <= 1200:
        return '#FF6800'
    else:
        return '#7F0000'


def plot_heaps_plotly(pack_result, heaps, title='', plot_heap_cube=True, plot_type=1):
    """
    使用plotly绘制结果
    :param pack_result:
    :param heaps:
    :param title:
    :param plot_heap_cube:
    :param plot_type:
    :return:
    """
    figs = []
    for b in pack_result.bins:
        # 1. 创建图形对象
        fig = go.Figure()

        # 1. 绘制bin
        PlotPainter.plotCube(fig, 0, 0, 0, float(b.width), float(b.height), float(b.depth), color="black", mode=1)

        # 2. 依次绘制heap
        for heap in b.items:

            if heap.typeof != 'heap':
                continue

            heap_name = heap.name

            # 2.1. 绘制heap所在的cube
            heap_x, heap_y, heap_z = heap.position
            col = heaps[heap_name][0][0][7]  # 原始颜色
            if plot_type == 1:
                col = 'orange'
            elif plot_type == 2:
                col = getCubeColor(0.)
            # 判断是否绘制heap所在的cube
            if plot_heap_cube:
                PlotPainter.plotCube(fig, float(heap_x), float(heap_y), float(heap_z),
                                     float(heap.width), float(heap.height), float(heap.depth),
                                     color=col, mode=1)

            # 2.2. 绘制heap中的cylinder
            for layer in heaps[heap_name]:
                for cylinder in layer:
                    cylinder_x, cylinder_y, cylinder_z = float(heap_x) + cylinder[4], float(heap_y) + cylinder[5], float(heap_z) + cylinder[6]
                    radius = float(cylinder[0]) / 2
                    PlotPainter.plotCylinder(fig, radius, cylinder_x + radius, cylinder_y, cylinder_z + radius,
                                             float(cylinder[1]), n_slices=80, n_sub=8,
                                             surface_kw={"color": col, "opacity": 1.0}, line_kw={"line_color": col, "line_width": 3})

        annotations = []
        # 3. 绘制额外的cubes
        for cube in b.items:
            if cube.typeof == 'cube':
                if cube.partno.startswith('CubeBox'):  # 预打包的cubes_heap
                    heap_x, heap_y, heap_z = float(cube.position[0]), float(cube.position[1]), float(cube.position[2])
                    heap_w, heap_h, heap_d = float(cube.dimension[0]), float(cube.dimension[1]), float(
                        cube.dimension[2])
                    col = getCubeColor(0.)
                    if plot_type == 1:
                        if cube.name.startswith('wooden'):
                            col = 'purple'
                        elif cube.name.startswith('tray'):
                            col = 'cyan'
                        elif cube.name.startswith('gray'):
                            col = 'gray'
                    if plot_heap_cube:
                        PlotPainter.plotCube(fig, heap_x, heap_y, heap_z, heap_w, heap_h, heap_d,
                                             color=col, mode=1, annotations=annotations)
                    for layer in heaps[cube.name]:
                        for son in layer:
                            son_x, son_y, son_z = son[4:7]
                            son_w, son_h, son_d = son[0:3]
                            if cube.rotation_type == 1:
                                son_w, son_h = son_h, son_w
                                son_x, son_y = son_y, son_x
                            PlotPainter.plotCube(fig, heap_x + son_x, heap_y + son_y, heap_z + son_z,
                                                 son_w, son_h, son_d, color=col, mode=2, lineWidth=2,
                                                 text='', annotations=annotations)
                else:
                    # 3.1 绘制零散的cubes
                    cube_x, cube_y, cube_z = cube.position
                    cube_w, cube_h, cube_d = cube.getDimension()
                    text = str(round(cube.pressure, 0)) if cube.pressure > 0 else ''
                    cube_color = cube.color
                    if plot_type == 2:
                        cube_color = getCubeColor(cube.pressure)
                    PlotPainter.plotCube(fig, float(cube_x), float(cube_y), float(cube_z),
                                         float(cube_w), float(cube_h), float(cube_d),
                                         color=cube_color, mode=2, lineWidth=2, text=text, annotations=annotations)

        # 4. 设置fig的布局属性
        padding = 10    # 坐标系范围pad
        limit = max(float(b.width), float(b.height), float(b.depth)) + padding
        fig.update_layout(title={
            'text': f"Bin({float(b.width)}x{float(b.height)}x{float(b.depth)})",
            'x': 0.5,  # 将标题置于水平方向的中央
            'xanchor': 'center',  # 设置水平方向的对齐方式为中央
            'y': 0.95  # 将标题置于垂直方向的顶部
        }, scene=dict(
            xaxis=dict(visible=True, range=[0, limit], tickfont=dict(size=12)),
            yaxis=dict(visible=True, range=[0, limit], tickfont=dict(size=12)),
            zaxis=dict(visible=True, range=[0, limit], tickfont=dict(size=12)),
            aspectmode='cube'
        ))

        figs.append(fig)

    return figs


def packing_items(params, sessions, token, user_ip='127.0.0.1', pre_packing_cubes=True, pre_min_num=20, top_num=5):

    preMinNum = pre_min_num
    topNum = top_num

    pack_start = time.time()
    matplotlib_time, plotly_time = 0., 0.

    prices = []

    # 1. 获取货物信息
    items = params.get('items', [])
    config_parameters = params.get('config_parameters', [])

    if sum([ie[4] for ie in items]) < pre_min_num:
        pre_packing_cubes = False

    print(items)

    if not items:
        return {
            'code': 101,
            'result': None,
            'description': '缺少货物参数'
        }
    # 2. 获取相关的参数
    radius1 = int(config_parameters['dish']['second_dish_radius_1']) / 100
    radius2 = int(config_parameters['dish']['second_dish_radius_2']) / 100
    all_cars = params['cars']
    print(all_cars)
    prices = [car['price'] for car in all_cars]
    cars = [(car['size'][0], car['size'][1], car['size'][2], car['loadbear'], car['price']) for car in all_cars]
    cars = sorted(cars, key=lambda x: x[4])

    ie_v, ie_w = 0, 0
    for ie in items:
        ie_w += ie[3] * ie[4]
        ie_v += ie[0] * ie[1] * ie[2] * ie[4]

    results = []
    # 3. 使用单个货车进行装箱
    suc_num = 0
    single_flags = []
    print("#" * 30)
    print('开始计算单车方案')
    print("#" * 30)
    for s in range(len(cars)):
        b = cars[s]
        box = Bin(f"car-{s}", (b[0], b[1], b[2]), b[3], 0, put_type=0, price=b[4])

        total_bin_v = box.getVolume()
        total_bin_w = box.max_weight
        if total_bin_v < ie_v or total_bin_w < ie_w:
            continue

        # 3.1 装箱
        heaps, pack_result = packing([box], items, radius1, radius2, pre_packing_cubes=pre_packing_cubes)

        b = pack_result.bins[0]
        # 3.2 判断装箱是否成功
        if len(b.items) >= pack_result.total_items:
            suc_num += 1
            success = True
        else:
            success = False
        single_flags.append(success)

        bin_v = float(b.width) * float(b.height) * float(b.depth)
        space_ratio = ie_v / bin_v

        # 3.4 计算承重利用率
        weight_ratio = ie_w / float(b.max_weight)
        if weight_ratio > 1.0 or space_ratio > 1.0:
            success = False

        # 3.5 车辆类型信息
        car_type = f"car{s}-{[a/1000 for a in cars[s][0:3]]}"

        # item_color = {}
        # for heap_name, layers in heaps.items():
        #     item_color[f"{'-'.join([str(im) for im in layers[0][0][:4]])}"] = layers[0][0][7]

        # 3.9 结果汇总
        results.append({
            'status': success,
            'bin_type': [car_type],
            'bin_num': [1],
            'space_ratio': space_ratio,
            'box_weight': float(b.max_weight),
            'goods_weight': ie_w,
            'weight_ratio': weight_ratio,
            'price': prices[s],
            'goods_volume': ie_v,
            'bin_volume': float(b.width * b.height * b.depth),
            # 'color': item_color,
            'heaps': str(pickle.dumps(heaps), encoding='latin1'),
            'packer': str(pickle.dumps(pack_result), encoding='latin1'),
            'car-num': 1
        })

        sessions[token] = results
        # print(token, sessions[token])

    # 4. 使用多个货车进行装箱
    old_multi_box = []

    total_num = top_num
    try_num = 0
    while suc_num < total_num:
        multi_boxes = []

        if not old_multi_box:
            print("#" * 30)
            print(f'开始生成2车方案')
            print("#" * 30)
            # 两车装箱方案
            for x in range(len(cars)):
                b1 = cars[x]
                for y in range(x, len(cars)):
                    b2 = cars[y]
                    multi_boxes.append([b1, b2])
                    # multi_boxes.append([Bin(f"car-{x}", (b1[0], b1[1], b1[2]), b1[3], 0, put_type=0, price=b1[4]),
                    #                     Bin(f"car-{y}", (b2[0], b2[1], b2[2]), b2[3], 0, put_type=0, price=b2[4])])
        else:
            print("#" * 30)
            print(f'开始生成{len(old_multi_box[0]) + 1}车方案')
            print("#" * 30)
            # 多车装箱方案
            for old_boxes in old_multi_box:
                for z in range(len(cars)):
                    other = cars[z]
                    ob = []
                    # for be in old_boxes:
                    #     ob.append(Bin(be.partno, (be.width, be.height, be.depth), be.max_weight, 0, put_type=0, price=be.price))
                    # for m in range(len(old_boxes)):
                    #     old_boxes[m].clearBin()
                    multi_boxes.append(old_boxes + [other])
                    # multi_boxes.append(old_boxes + [Bin(f"car-{z}", (other[0], other[1], other[2]), other[3], 0, put_type=0, price=other[4])])
        # old_multi_box = copy.deepcopy(multi_boxes)
        old_multi_box = multi_boxes

        # 按照成本排序
        multi_boxes = sorted(multi_boxes, key=lambda x: sum([su[4] for su in x]))
        # multi_boxes = sorted(multi_boxes, key=lambda x: sum([su.price for su in x]))

        print("#" * 30)
        print(f'开始计算{len(multi_boxes[0])}车方案')
        print("#" * 30)
        for indx, box in enumerate(multi_boxes):

            if suc_num >= total_num:
                break

            total_bin_v = sum([be[0] * be[1] * be[2] for be in box])
            # total_bin_v = sum([float(be.getVolume()) for be in box])
            total_bin_w = sum([be[3] for be in box])
            # total_bin_w = sum([float(be.max_weight) for be in box])
            if total_bin_v < ie_v or total_bin_w < ie_w:
                continue

            print(try_num, suc_num, total_num, len(box))
            # 4.1 装箱
            heaps, pack_result = packing(box, items, radius1, radius2, pre_packing_cubes=pre_packing_cubes)
            try_num += 1

            # 4.2 判断装箱是否成功
            if sum([len(ie.items) for ie in pack_result.bins]) == pack_result.total_items:
                success = True
                suc_num += 1
            else:
                success = False

            # 4.3 计算空间利用率
            # ie_v, ie_w = 0., 0.
            # bin_v, bin_w = 0., 0.
            price = sum([be.price for be in pack_result.bins])

            if ie_w / total_bin_w > 1 or ie_v / total_bin_v > 1:
                success = False

            if price in prices:
                if success:
                    suc_num -= 1
            else:
                prices.append(price)
                # 4.4 车辆类型
                bin_type, bin_num = [], []
                for bi in pack_result.bins:
                    bin_type.append(f"car-{str([float(bi.width)/1000, float(bi.height)/1000, float(bi.depth) / 1000])}")
                    bin_num.append(1)

                results.append({
                    'status': success,
                    'bin_type': bin_type,
                    'bin_num': bin_num,
                    'space_ratio': ie_v / total_bin_v,
                    'box_weight': total_bin_w,
                    'goods_weight': ie_w,
                    'weight_ratio': ie_w / total_bin_w,
                    'price': price,
                    'goods_volume': ie_v,
                    'bin_volume': total_bin_v,
                    'color': {},
                    'heaps': str(pickle.dumps(heaps), encoding='latin1'),
                    'packer': str(pickle.dumps(pack_result), encoding='latin1'),
                    'car-num': len(box)
                })
                sessions[token] = results
                # print(token, sessions[token])

    pack_end = time.time()
    total_time = pack_end - pack_start
    print(f"装箱算法耗时{round(total_time - plotly_time - matplotlib_time, 3)}秒，matplotlib绘图耗时{round(matplotlib_time, 3)}秒，plotly绘图耗时{round(plotly_time, 3)}秒，总耗时{round(total_time, 3)}秒")
    results = list(filter(lambda x: x['status'], results))
    # return sorted(results, key=lambda x: x['price'])


def calculatePressureArea(item1, item2):
    """
    计算item1与item2的受力面积(item1在下面，item2在上面)
    :param item1:
    :param item2:
    :return:
    """
    x1, y1, z1 = item1.position
    x1, y1, z1 = float(x1), float(y1), float(z1)
    x2, y2, z2 = item2.position
    x2, y2, z2 = float(x2), float(y2), float(z2)
    w1, h1, d1 = float(item1.width), float(item1.height), float(item1.depth)
    w2, h2, d2 = float(item2.width), float(item2.height), float(item2.depth)

    x_min1, x_max1 = x1, x1 + w1
    y_min1, y_max1 = y1, y1 + h1
    x_min2, x_max2 = x2, x2 + w2
    y_min2, y_max2 = y2, y2 + h2

    # 两个矩形交集部分的坐标
    x_min, x_max = max(x_min1, x_min2), min(x_max1, x_max2)
    y_min, y_max = max(y_min1, y_min2), min(y_max1, y_max2)


    # if x1 > x2:
    #     x1, x2 = x2, x1
    #     w1, w2 = w2, w1
    # if y1 > y2:
    #     y1, y2 = y2, y1
    #     h1, h2 = h2, h1

    # 1. 判断item1和item2在z轴上是否有交集
    if z1 + d1 != z2:
        return 0
    # # 2. 计算item1和item2在xoy面上的交集
    # if x1 + w1 > x2 and y2 + h2 > y1:
    #     # 2.1 计算重叠部分面积(即受力面积)
    #     return (x1 + w1 - x2) * (y2 + h2 - y1)
    # return 0

    if x_max > x_min and y_max > y_min:
        return (x_max - x_min) * (y_max - y_min)
    return 0.


def assignWeight(weight, parents_map, children_map, total_items, parent_index):

    if not children_map[parent_index]:
        return {}

    results = {idx: 0.0 for idx in range(total_items)}
    # 1. 将weight分配到直接子节点
    for children_index in children_map[parent_index]:
        new_weight = parents_map[children_index][parent_index] * weight
        results[children_index] += new_weight

        # 2. 将分配到每个直接子节点的压力再分配到间接子节点
        ret = assignWeight(new_weight, parents_map, children_map, total_items, children_index)
        results = {ix: ret.get(ix, 0) + num for ix, num in results.items()}
    return results


def check_pressure(bin, check_type=1):
    """
    检查给定bin中的所有item受力分析是否正确
    :param bin:
    :param check_type:
    :return:
    """

    # 1. 对于每个item，建立父节点、子节点的树状结构
    parents_map, children_map = {idx: {} for idx in range(len(bin.items))}, {idx: [] for idx in range(len(bin.items))}

    for index1, item1 in enumerate(bin.items):
        areas = {}
        for index2, item2 in enumerate(bin.items):
            if id(item1) == id(item2):
                continue
            pressure_area = calculatePressureArea(item2, item1)
            if pressure_area > 0:
                # item1为item2的父节点
                areas[index2] = pressure_area
                # 1.1 更新item1的子节点
                children_map[index1].append(index2)
        # 1.2 更新item2的父节点
        total_pressure_area = sum(list(areas.values()))
        if check_type == 2:
            total_pressure_area = float(item1.width) * float(item1.height)

        for idx, area in areas.items():
            parents_map[idx][index1] = area / total_pressure_area

    # 2. 根据每个item的父节点和子节点分析受重
    pressure_map = {idx: 0. for idx in range(len(bin.items))}
    for index, item in enumerate(bin.items):
        # 1. 将当前item的重量分配给它的子节点
        ret = assignWeight(float(item.weight), parents_map, children_map, len(bin.items), index)
        pressure_map = {ix: num + ret.get(ix, 0) for ix, num in pressure_map.items()}


    # 3. 对比受力分析是否一致
    check_pressure, real_pressure = [], []
    for index, item in enumerate(bin.items):
        if type(item.pressure) == float:
            real_pressure.append(item.pressure)
        else:
            real_pressure.append(float(item.pressure))
        check_pressure.append(pressure_map.get(index, 0))
    return {
        'check_pressure': check_pressure,
        'origin_pressure': real_pressure,
        'is_correct': np.allclose(real_pressure, check_pressure)
    }


if __name__ == '__main__':
    # items = [[4, 11, 4 * math.pi, 800, 2, 0], [5, 25, 5 * math.pi, 100, 0, 0], [5, 6, 7, 10, 4, 1], [5, 6, 7, 10, 4, 3], [5, 6, 7, 10, 4, 2]]
    # items = [[4, 11, 12.566370614359172, 800, 4, 0], [5, 6, 2, 20, 4, 2], [11, 9, 9, 200, 8, 3], [11, 8, 8, 300, 32, 1]]
    # items = [[4, 11, 12.566370614359172, 800, 4, 0], [5, 6, 2, 20, 4, 2], [11, 9, 9, 200, 3, 3], [11, 8, 8, 500, 6, 1]]
    # box = [[66, 26, 24, 50000 * 1000, 15000], [66, 26, 24, 50000 * 1000, 24000]]

    # 案例2
    # items = [[23, 7.2, 8.2, 518, 96, 1], [20.6, 7.2, 8.2, 450, 1, 1], [2.6, 7.2, 8.2, 45, 1, 1]]
    # box = [[175, 30, 30, 35000, 15000], [96, 24, 30, 18000, 15000]]
    # box = [[175, 30, 30, 30000, 15000], [175, 30, 30, 30000, 15000]]

    # 案例4
    # items = [[5, 10, 5 * math.pi, 100, 6, 0], [10,10,10.2,258,160,2], [10,10,7.65,199,1,2],[10,10,7.65,178,5,2],[10,10,7.65,151,1,2],[10,10,7.65,45,1,2],[10,10,5.1,125,1,2],[10,10,5.1,99,2,2],[10,10,2.55,71,7,2]]
    # box = [[175, 30, 30, 30000, 15000], [135, 24, 30, 30000, 15000]]

    # items = [[1, 1, 1, 350, 64, 1], [1, 1, 1, 900, 64, 1], [2, 2, 1, 30, 9, 1], [2, 2, 1, 350, 21, 1]]

    # items = [[2, 2, 1, 30, 9, 1], [2, 2, 1, 350, 21, 1],
    #          [1, 1, 1, 350, 64, 1], [1, 1, 1, 900, 64, 1]]
    # box = [[6, 5, 4, 100000, 10000], [6, 5, 4, 100000, 10000],
    #        [6, 5, 4, 100000, 10000], [6, 5, 4, 100000, 10000],
    #        [6, 5, 4, 100000, 10000], [6, 5, 4, 100000, 10000]]

    # items = [[0.8, 1.6, 1.2, 300, 36, 2]] * 1
    # box = [[6.8, 2.6, 3.0, 100000, 10000]] * 3

    # items = [[10, 10, 10.6, 416, 32, 3]]
    items = [[1200, 800, 700, 403, 1, 1]]
    # items = [[1000, 1000, 600, 620, 2, 2], [1000, 1000, 1000, 416, 32, 2], [1000, 1000, 800, 315, 1, 2], [1000, 1000, 800, 255, 1, 2]]
    # box = [[7600, 2400, 3000, 100000, 10000]] * 10
    # box = [[9600, 2400, 3000, 100000, 10000], [9600, 2400, 3000, 100000, 10000], [9600, 2400, 3000, 100000, 10000]]
    box = [[13500, 3000, 3000, 30000, 10000]]
    #
    radius_threshold1, radius_threshold2 = 1, 7
    start_time = time.time()
    heaps, result = packing(box, items, radius_threshold1, radius_threshold2, pre_packing_cubes=True)
    result.items = [result.bins[0].items[0]]
    result.pack(bigger_first=False,
                distribute_items=False,
                fix_point=True,
                check_stable=True,
                support_surface_ratio=1.,
                number_of_decimals=2)
    end_time = time.time()
    print(f"耗时{round(end_time - start_time, 3)}秒")


    #
    # total = 0
    # for i in range(10):
    #     start = time.time()
    #     heaps, result = packing(box, items, radius_threshold1, radius_threshold2)
    #     end = time.time()
    #     t = round(end - start, 3)
    #     total += t
    #     print(t)
    # print(total / 10)


    # all_items = [[[0.8, 0.4, 0.8 * math.pi, 30, 1, 0]],
    #              [[0.8, 0.4, 0.8 * math.pi, 30, 1, 0], [0.8, 0.4, 0.4, 350, 1, 1], [0.8, 0.4, 0.4, 350, 6, 2], [0.8, 0.4, 0.4, 900, 2, 3]],
    #              [[0.8, 0.4, 0.8 * math.pi, 30, 1 * 5, 0], [0.8, 0.4, 0.4, 350, 1 * 5, 1], [0.8, 0.4, 0.4, 350, 6 * 5, 2], [0.8, 0.4, 0.4, 900, 2 * 5, 3]],
    #              [[0.8, 0.4, 0.8 * math.pi, 30, 1 * 10, 0], [0.8, 0.4, 0.4, 350, 1 * 10, 1], [0.8, 0.4, 0.4, 350, 6 * 10, 2], [0.8, 0.4, 0.4, 900, 2 * 10, 3]],
    #              [[0.8, 0.4, 0.8 * math.pi, 30, 1 * 20, 0], [0.8, 0.4, 0.4, 350, 1 * 20, 1], [0.8, 0.4, 0.4, 350, 6 * 20, 2], [0.8, 0.4, 0.4, 900, 2 * 20, 3]],
    #              [[0.8, 0.4, 0.8 * math.pi, 30, 1 * 50, 0], [0.8, 0.4, 0.4, 350, 1 * 50, 1], [0.8, 0.4, 0.4, 350, 6 * 50, 2], [0.8, 0.4, 0.4, 900, 2 * 50, 3]],]
    # all_box = [[[4.2, 1.9, 2.5, 100000, 10000]] * 20,
    #            [[6.8, 2.4, 3.0, 100000, 10000]] * 20,
    #            [[7.6, 2.4, 3.0, 100000, 10000]] * 20,
    #            [[9.6, 2.4, 3.0, 100000, 10000]] * 20,
    #            [[13.5, 2.4, 3.0, 100000, 10000]] * 20,
    #            [[17.5, 3.0, 3.0, 100000, 10000]] * 20]
    #
    # run_times = 100
    # for box in all_box:
    #     for items in all_items:
    #         total = 0
    #         for i in range(run_times):
    #             start = time.time()
    #             heaps, result = packing(box, items, radius_threshold1, radius_threshold2)
    #             end = time.time()
    #             t = round(end - start, 3)
    #             total += t
    #             # print(t)
    #         # print(total)
    #         cylinder = items[0][4]
    #         if len(items) == 4:
    #             wooden = items[1][4]
    #             tray = items[2][4]
    #             paper = items[3][4]
    #         else:
    #             wooden = 0
    #             tray = 0
    #             paper = 0
    #         car = box[0][0]
    #         content = f"{car},{cylinder + wooden + tray + paper},{cylinder},{wooden},{tray},{paper},{round(total / run_times, 3)}\n"
    #         with open('./提升后_100.txt', 'a') as fp:
    #             fp.write(content)

    # 使用matplotlib绘图
    # for b in result.bins:
    #     if not b.items:
    #         continue
    #     plot_heaps_matplot(b, heaps, '', True)[0].show()

    # plot_heaps_plotly(result, heaps)[0].show()
